package game.square;

import java.math.BigDecimal;
import java.util.Set;

import game.square.temperature.Temperature;

public interface RoomSquare extends Square {
	
	/**
	 * Return the temperature of this room square.
	 */
	public abstract Temperature getTemperature();

	/**
	 * Set the temperature of this square to the given temperature.
	 * 
	 * @param 	temperature
	 *          The temperature that represents the temperature of this square, 
	 *          before transition to a new equilibrium starts.
	 * @post  	..
	 *        | for each square in getRoom()
	 *        | 	if( square.getTemperature().getTemperatureValue(TemperatureScale.CELCIUS) < 
	 *        |			temperature.getTemperatureValue(TemperatureValue(TemperatureScale.CELCIUS))
	 *        |				square.getTemperature().getTemperatureValue(TemperatureScale.CELCIUS) <= 
	 *        |						(new square).getTemperature().getTemperatureValue(TemperatureScale.CELCUIS)
	 *        |				&&					
	 *        |				(new square).getTemperature().getTemperatureValue(TemperatureScale.CELCUIS)<=
	 *        |						temperature.getTemperatureValue(TemperatureScale.CELCIUS)       					
	 * @post	..
	 * 		  | for each square in getRoom()
	 *		  | 	if( square.getTemperature().getTemperatureValue(TemperatureScale.CELCIUS) > 
	 *        |			temperature.getTemperatureValue(TemperatureValue(TemperatureScale.CELCIUS))
	 *        |				square.getTemperature().getTemperatureValue(TemperatureScale.CELCIUS) >= 
	 *        |						(new square).getTemperature().getTemperatureValue(TemperatureScale.CELCUIS)
	 *        |				&&					
	 *        |				(new square).getTemperature().getTemperatureValue(TemperatureScale.CELCUIS)>=
	 *        |						temperature.getTemperatureValue(TemperatureScale.CELCIUS)
	 * @throws	IllegalStateException
	 * 			The given temperature is not effective
	 * 		  | temperature == null
	 */
	public abstract void setTemperature(Temperature inScale);

	/**
	 * Set the humidity of this square to the given humidity.
	 * 
	 * @param 	humidity
	 *          The humidity that represents the humidity of this square, 
	 *          before transition to a new equilibrium starts.
	 * @post  	..
	 *        | For each square in getRoom()
	 *        | 	if( square.getHumidity().compareTo(getHumidity()) == -1) 
	 *        |				square.getHumidity().compareTo((new square).getHumidity()) == -1 
	 *        |				&&					
	 *        |				(new square).getHumidity().compareTo(humidity)== -1					
	 * @post	..
	 * 		  | For each square in getRoom()
	 *		  | 	if( square.getHumidity().compareTo(humidity) == 1 )
	 *        |				square.getHumidity().compareTo((new square).getHumidity()) == 1 
	 *        |				&&					
	 *        |				(new square).getHumidity().compareTo(humidity) == 1
	 */
	public abstract void setHumidity(BigDecimal newHumidity);

	/**
	 * Return the humidity of this room square.
	 */
	public abstract BigDecimal getHumidity();

	/**
	 * Return the squares that belong to the same room as this square.
	 * 
	 * @return {squares in Square | square.isInSameRoomAs(this)}
	 */
	public abstract Set<RoomSquare> getRoom();
	
	/**
	 * Checks if the given square is in the same room as this square. The
	 * complexity is linear with the amount of squares in the room of this
	 * square.
	 * 
	 * @param 	square
	 * 			The square to check.
	 * @return	Return true if the given square borders this square and 
	 * 			the border between them is an open border
	 *		  | if (square.isDirectRoomNeighborFrom(this))
	 *		  |		result == true
	 * @result	Else return true if a the square this square borders 
	 * 			with an openable border, is in the same room as the given square.
	 * 		  | For someSquare in Square 
	 * 		  |		if (someSquare.isDirectRoomNeighborFrom(this))
	 * 		  |			if (someSquare.isInSameRoomAs(square))
	 * 		  |				result == true
	 * @result	Else, the result is false.
	 * 		  |	else result == false
	 */	public abstract boolean isInSameRoomAs(Square square);

	/**
	 * Set all squares belonging to the same room as this square, to their equilibrium
	 * value.
	 * 
	 * @param	partOfRoomInEquilibrium
	 * 			The part of the room of this square, where all squares are in equilibrium
	 * 			with this square.
	 * @param	otherPartOfRoom
	 * 			The part of the room of this square, with the squares that aren't in 
	 * 			equilibrium with this square, but are in equilibrium with each other.
	 * @pre		The union of the two given sets contains only the squares of this room.
	 * 		  | getRoom().containsAll(partOfRoomInEquilibrium) && 
	 * 		  |		getRoom().containsAll(otherPartOfRoom) && 
	 * 		  |		(getRoom().size() == partOfRoomInEquilibrium.size() + otherPartOfRoom.size()
	 * @post	If there is at least one other square belonging to the room of this square, then
	 * 			all temperatures and humidities of the squares that belong to the room are 
	 * 			physically equal to each other.
	 * 		  | if (getRoom().size() != 1)
	 * 		  |		then for each square in getRoom()
	 * 		  |			new.getTemperature().hasEqualTemperatureValueAs(new.getTemperature())
	 * 		  |			&& new.getHumidity().compareTo(new.getHumidity()) == 0
	 * 		  |			
	 * @post	If there is at least one other square belonging to the room of this square, then
	 * 			all temperatures and humidities of the squares that belong to the room are set
	 * 			to some sort of average of their current value.
	 * 		  | if (getRoom().size() != 1)
	 * 		  |		then for each square in getRoom()
	 * 		  |			new.getTemperature() = this.getTemperature().calculateWeightedAverageWith(
	 * 		  |						otherPartOfRoom.iterator().next().getTemperature(),
	 * 		  |						partOfRoomInEquilibrium.size(),	otherPartOfRoom.size())
	 * 		  |			&&
	 * 		  |			new.getHumidity() = this.getHumidity()
	 * 		  |						multiply(BigDecimal.valueOf(partOfRoomInEquilibrium.size())).
	 * 		  |						add(otherPartOfRoom.iterator().next().getHumidity().
	 * 		  |							multiply(BigDecimal.valueOf(otherPartOfRoom.size()))).
	 * 		  |						divide(BigDecimal.valueOf(partOfRoomInEquilibrium.size()).
	 * 		  |							add(BigDecimal.valueOf(otherPartOfRoom.size())))
	 */
	public abstract void setRoomEquilibrium(Set<RoomSquare> thisPartOfRoom,
			Set<RoomSquare> neighborPartOfRoom);
	
	/**
	 * Set the humidity of this room square to the given humidity.
	 *
	 * @param 	humidity
	 * 			The new value for the humidity
	 * @pre 	The given humidity must be a valid humidity for a square
	 * 		  | isValidHumidity(humidity)
	 * @pre		The given humidity must be some kind of average of the humidities of the
	 * 			all the squares in the room with this square.
	 * @post  	The humidity of this square is equal to the given humidity 
	 * 			rounded with a precision of the number of decimals for the humidity
	 * 			plus one, when the value of the given humidity is less than or equal
	 * 			to 9 and rounded with a precision of of the number of decimals for the 
	 * 			humidity plus two when the value of the given humidity is greater than
	 * 			9.
	 *        | new.getHumidity() == humidity.round(new MathContext(getRequiredPrecision(humidity)))
	 */
	public abstract void setEquilibriumHumidity(BigDecimal humidity);

	/**
	 * Add all other room squares to the given room.
	 * 
	 * @param	roomPart
	 * 			The roomPart to add the other room squares to.
	 * @pre		This room square must be an element of the set.
	 * @post	..
	 * 		  | roomPart.equals({roomSquares in RoomSquare | roomSquare.isInSameRoomAs(this)})
	 */
	public abstract void addOtherRoomSquaresTo(Set<RoomSquare> partOfRoom);
}
